3 Exploratory Data Analysis (EDA)

The image above is from R4DS(2e) by Hadley Wickham, Mine Çetinkaya-Rundel and Garrett Grolemund
The image above is from R4DS(2e) by Hadley Wickham, Mine Çetinkaya-Rundel and Garrett Grolemund

EDA is an iterative cycle that helps you understand what your data says. When you do EDA, you:

  1. Generate questions about your data

  2. Search for answers by visualising, transforming, and/or modeling your data

  3. Use what you learn to refine your questions and/or generate new questions

EDA is an important part of any data analysis. You can use EDA to make discoveries about the world; or you can use EDA to ensure the quality of your data, asking questions about whether the data meets your standards or not. (Posit Primers: EDA)

3.1 GDP and GDP per Capita

  1. GDP, PPP (constant 2017 international $): NY.GDP.MKTP.PP.KD

  2. Population, total: SP.POP.TOTL

  3. Calculate GDP per Capita

    • GDP per capita, PPP (constant 2017 international $): NY.GDP.PCAP.PP.KD
  • GDP, PPP (constant 2017 international $) PPP GDP is gross domestic product converted to international dollars using purchasing power parity rates. An international dollar has the same purchasing power over GDP as the U.S. dollar has in the United States. GDP is the sum of gross value added by all resident producers in the country plus any product taxes and minus any subsidies not included in the value of the products. It is calculated without making deductions for depreciation of fabricated assets or for depletion and degradation of natural resources. Data are in constant 2017 international dollars. ID: NY.GDP.MKTP.PP.KD

  • Population, total Total population is based on the de facto definition of population, which counts all residents regardless of legal status or citizenship. The values shown are midyear estimates. ID: SP.POP.TOTL

df_gdppcap <- WDI(indicator = c(gdp = "NY.GDP.MKTP.PP.KD", pop = "SP.POP.TOTL", gdppcap = "NY.GDP.PCAP.PP.KD"), extra = TRUE)
write_csv(df_gdppcap, "data/gdppcap.csv")
df_gdppcap <- read_csv("data/gdppcap.csv")
Rows: 16758 Columns: 15── Column specification ─────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr  (7): country, iso2c, iso3c, region, capital, income, lending
dbl  (6): year, gdp, pop, gdppcap, longitude, latitude
lgl  (1): status
date (1): lastupdated
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
str(df_gdppcap)
spc_tbl_ [16,758 × 15] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
 $ country    : chr [1:16758] "Afghanistan" "Afghanistan" "Afghanistan" "Afghanistan" ...
 $ iso2c      : chr [1:16758] "AF" "AF" "AF" "AF" ...
 $ iso3c      : chr [1:16758] "AFG" "AFG" "AFG" "AFG" ...
 $ year       : num [1:16758] 2014 2012 2009 2013 1971 ...
 $ status     : logi [1:16758] NA NA NA NA NA NA ...
 $ lastupdated: Date[1:16758], format: "2023-09-19" "2023-09-19" "2023-09-19" "2023-09-19" ...
 $ gdp        : num [1:16758] 7.02e+10 6.47e+10 4.99e+10 6.83e+10 NA ...
 $ pop        : num [1:16758] 32716210 30466479 27385307 31541209 11015857 ...
 $ gdppcap    : num [1:16758] 2144 2123 1824 2165 NA ...
 $ region     : chr [1:16758] "South Asia" "South Asia" "South Asia" "South Asia" ...
 $ capital    : chr [1:16758] "Kabul" "Kabul" "Kabul" "Kabul" ...
 $ longitude  : num [1:16758] 69.2 69.2 69.2 69.2 69.2 ...
 $ latitude   : num [1:16758] 34.5 34.5 34.5 34.5 34.5 ...
 $ income     : chr [1:16758] "Low income" "Low income" "Low income" "Low income" ...
 $ lending    : chr [1:16758] "IDA" "IDA" "IDA" "IDA" ...
 - attr(*, "spec")=
  .. cols(
  ..   country = col_character(),
  ..   iso2c = col_character(),
  ..   iso3c = col_character(),
  ..   year = col_double(),
  ..   status = col_logical(),
  ..   lastupdated = col_date(format = ""),
  ..   gdp = col_double(),
  ..   pop = col_double(),
  ..   gdppcap = col_double(),
  ..   region = col_character(),
  ..   capital = col_character(),
  ..   longitude = col_double(),
  ..   latitude = col_double(),
  ..   income = col_character(),
  ..   lending = col_character()
  .. )
 - attr(*, "problems")=<externalptr> 
df_gdppcap |> select(region, income, lending) |> lapply(unique)
$region
[1] "South Asia"                 "Aggregates"                 "Europe & Central Asia"      "Middle East & North Africa"
[5] "East Asia & Pacific"        "Sub-Saharan Africa"         "Latin America & Caribbean"  "North America"             
[9] NA                          

$income
[1] "Low income"          "Aggregates"          "Upper middle income" "Lower middle income" "High income"        
[6] NA                    "Not classified"     

$lending
[1] "IDA"            "Aggregates"     "IBRD"           "Not classified" "Blend"          NA              
COUNTRY <- "World"
df_gdppcap |> filter(country == COUNTRY) |>
  ggplot(aes(year, gdppcap)) + geom_line()

COUNTRY <- "World"
df_gdppcap |> filter(country == COUNTRY) |>
  ggplot(aes(year, pop)) + geom_line()

3.1.0.1 Exercise.

Write your observations and questions.

3.1.1 GDP Per Capita

df_gdppcap2 <- df_gdppcap |> drop_na(pop) |> 
  mutate(PCAP = gdp/pop, .after = gdppcap)
df_gdppcap2

3.1.1.1 Check against GDP per capita, PPP

df_gdppcap2 |> drop_na(gdppcap, PCAP) |> mutate(near = near(gdppcap, PCAP)) |> 
  summarize(numberofdata = n(), sum(near))
df_gdppcap2 |> filter(!near(gdppcap, PCAP))

3.1.1.2 Exercise.

Write your observations and questions.

3.1.2 Visualization

Two useful questions.

  1. What type of variation occurs within my variables?

  2. What type of covariation occurs between my variables?

See Link.

3.1.2.1 Ranks.

arrange(desc(gdp)) is to reorder in descending order of gdp, arrange(gdp) in ascending order.

df_gdppcap |> filter(year == 2022, region != "Aggregates") |> 
  drop_na(gdp) |> arrange(desc(gdp))

3.1.2.2 Exercises.

  1. Find the top 10 of the countries with the highest GDP per capita.

  2. Find the top 10 of the countries with the lowest GDP per capita.

  3. Find the top 10 of the countries with the largest population.

  4. Find the top 10 of the countries with the smallest population.

3.1.2.3 Scatter Plot

What type of covariation occurs between my variables?

df_gdppcap2 |> filter(year == 2022, region !="Aggregates") |> 
  drop_na(gdp, pop) |> 
  ggplot(aes(pop, gdp)) + geom_point()

df_gdppcap2 |> filter(year == 2022, region !="Aggregates") |> 
  drop_na(gdp, pop) |> 
  ggplot(aes(pop, gdp)) + geom_point() + 
  scale_x_log10() + scale_y_log10()

df_gdppcap2 |> filter(year == 2022, region !="Aggregates") |> 
  drop_na(gdp, pop) |> 
  ggplot(aes(pop, gdp)) + geom_point() + 
  geom_smooth(method = "lm", se = FALSE) +
  scale_x_log10() + scale_y_log10()

df_gdppcap2 |> filter(year == 2020, region !="Aggregates") |> drop_na(gdp, pop) |> 
  ggplot(aes(pop, gdp, color = region)) + geom_point() + 
  scale_x_log10() + scale_y_log10()

df_gdppcap2 |> filter(year == 2020, region !="Aggregates") |> 
  drop_na(gdp, pop) |> 
  ggplot(aes(pop, gdp, color = region, shape = income)) + geom_point() + 
  scale_x_log10() + scale_y_log10()

df_gdppcap2 |> filter(year == 2020, region !="Aggregates") |> 
  drop_na(gdp, gdppcap, pop) |> 
  ggplot(aes(gdppcap, gdp, color = region, size = pop)) + geom_point() + 
  scale_x_log10() + scale_y_log10()

install.packages("plotly")
library(plotly)
test <- df_gdppcap2 |> filter(year == 2020, region !="Aggregates") |> drop_na(gdp, pop) |> 
  ggplot(aes(color = country, shape = region, pop, gdp)) + geom_point() + 
  scale_x_log10() + scale_y_log10() + theme(legend.position = "none")
test |> ggplotly()
Warning: The shape palette can deal with a maximum of 6 discrete values because more than 6 becomes difficult to
discriminate; you have 7. Consider specifying shapes manually if you must have them.

3.1.2.4 Variation 1. Histogram

df_gdppcap |> filter(year == 2022, region != "Aggregates") |> drop_na(gdp) |> 
  ggplot(aes(gdp)) + geom_histogram()

df_gdppcap |> filter(year == 2022, region != "Aggregates") |> drop_na(gdppcap) |> 
  ggplot(aes(gdppcap)) + geom_histogram()

df_gdppcap |> filter(year == 2022, region != "Aggregates") |> drop_na(gdp) |> 
  ggplot(aes(gdp)) + geom_histogram() + scale_x_log10()

3.1.2.5 Exercises.

  1. Change bins, i.e., geom_histogram(bins = 20), etc.
df_gdppcap |> filter(year == 2022, region != "Aggregates") |> drop_na(gdp) |> 
  ggplot(aes(gdp)) + geom_histogram(bins = 20) + scale_x_log10()
  1. Create a similar histogram of gdppcap by using scale_x_log10() and adjust the number of bins.
df_gdppcap |> filter(year == 2022, region != "Aggregates") |> drop_na(gdppcap) |> 
  ggplot(aes(gdppcap)) + geom_histogram() + scale_x_log10()
  1. Create a similar histogram for population.
  2. Write your observations and comments.

3.1.2.6 Extra.

df_gdppcap |> filter(year == 2022,region != "Aggregates") |> drop_na(pop) |> 
  group_by(region) |> 
  ggplot(aes(pop, fill = region)) + geom_histogram(col = "black", linewidth = 0.2) + scale_x_log10()

3.1.2.7 Variation 2. Boxplot

df_gdppcap2 |> filter(year %in% c(1990,2000, 2010, 2020)) |> drop_na(gdppcap) |> 
  ggplot(aes(gdppcap, factor(year))) + geom_boxplot() + scale_x_log10()

df_gdppcap2 |> filter(year %in% c(1990,2000, 2010, 2020)) |> drop_na(gdppcap) |> 
  ggplot(aes(gdppcap, factor(year))) + geom_boxplot() + scale_x_log10() +
  labs(title = "Distribution of the GDP per Capita of Countries", subtitle = "Year 1990, 2000, 2010, 2020", 
       y = "Year", x = "GDP per capita in log10 scale")

df_gdppcap2 |> filter(year == 2020) |> drop_na(gdppcap) |> 
  filter(income != "Aggregates") |> 
  ggplot(aes(gdppcap, income, fill = income)) + geom_boxplot() + scale_x_log10() +
  theme(legend.position = "none")

df_gdppcap2 |> filter(year == 2020) |> drop_na(gdppcap) |> 
  filter(income != "Aggregates") |> 
  ggplot(aes(gdppcap, factor(income, levels = c("High income", "Upper middle income", "Lower middle income", "Low income")), fill = income)) + geom_boxplot() + scale_x_log10() +
  labs(y = "") +
  theme(legend.position = "none")

df_gdppcap2 |> filter(year == 2020) |> drop_na(gdp) |> 
  filter(income != "Aggregates") |> 
  ggplot(aes(gdp, region, fill = region)) + geom_boxplot() + scale_x_log10() +
  theme(legend.position = "none")

3.2 CO2 Emissions Per Capita vs GDP Per Capita

  1. CO2 emissions (metric tons per capita): EN.ATM.CO2E.PC

  2. GDP per capita, PPP (constant 2017 international $): NY.GDP.PCAP.PP.KD

  • CO2 emissions (metric tons per capita) Carbon dioxide emissions are those stemming from the burning of fossil fuels and the manufacture of cement. They include carbon dioxide produced during consumption of solid, liquid, and gas fuels and gas flaring. EN.ATM.CO2E.PC

  • GDP per capita, PPP (constant 2017 international $) GDP per capita based on purchasing power parity (PPP). PPP GDP is gross domestic product converted to international dollars using purchasing power parity rates. An international dollar has the same purchasing power over GDP as the U.S. dollar has in the United States. GDP at purchaser’s prices is the sum of gross value added by all resident producers in the country plus any product taxes and minus any subsidies not included in the value of the products. It is calculated without making deductions for depreciation of fabricated assets or for depletion and degradation of natural resources. Data are in constant 2017 international dollars. ID: NY.GDP.PCAP.PP.KD

3.2.1 Importing Data

df_co2gdp <- WDI(indicator = c(co2pcap = "EN.ATM.CO2E.PC", gdppcap = "NY.GDP.PCAP.PP.KD"), extra = TRUE)
write_csv(df_co2gdp, "data/co2gdp.csv")
df_co2gdp <- read_csv("data/co2gdp.csv")
Rows: 16758 Columns: 14── Column specification ─────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr  (7): country, iso2c, iso3c, region, capital, income, lending
dbl  (5): year, co2pcap, gdppcap, longitude, latitude
lgl  (1): status
date (1): lastupdated
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.

3.2.2 Visualization by Line Graphs

COUNTRY <- "World"
df_co2gdp |> filter(country == COUNTRY) |>
  ggplot(aes(year, co2pcap)) + geom_line()

ISO2C <- c("JP", "CN", "ID", "UK", "US", "DE", "FR")
df_co2gdp |> filter(iso2c %in% ISO2C) |> drop_na(co2pcap) |>
  ggplot(aes(year, co2pcap, linetype = iso2c)) + geom_line()

3.2.2.1 Exercises.

  1. Change iso2c codes to those you want to investigate. Use df_codes under Environment
  2. Change linetype to col.

3.2.3 Scatterplot for Covariation

df_co2gdp |> filter(year == 2020) |> drop_na(co2pcap) |>
  ggplot(aes(gdppcap, co2pcap)) + geom_point()

df_co2gdp |> filter(year == 2020) |> 
  drop_na(gdppcap, co2pcap) |>
  ggplot(aes(gdppcap, co2pcap)) + geom_point() +
  scale_x_log10() + scale_y_log10()

3.2.3.1 Scatterplot with a regression line

df_co2gdp |> filter(year == 2020) |> 
  drop_na(gdppcap, co2pcap) |>
  ggplot(aes(gdppcap, co2pcap)) + geom_point() +
  geom_smooth(method = "lm", formula = 'y~x', se = FALSE) +
  scale_x_log10() + scale_y_log10()

3.2.3.2 Summary of a linear model

df_co2gdp |> filter(year == 2020) |> drop_na(gdppcap, co2pcap) |>
  lm(log10(co2pcap)~log10(gdppcap), data = _) |> summary()

Call:
lm(formula = log10(co2pcap) ~ log10(gdppcap), data = drop_na(filter(df_co2gdp, 
    year == 2020), gdppcap, co2pcap))

Residuals:
     Min       1Q   Median       3Q      Max 
-0.60778 -0.15660 -0.00651  0.16129  0.59437 

Coefficients:
               Estimate Std. Error t value Pr(>|t|)    
(Intercept)    -4.31545    0.13386  -32.24   <2e-16 ***
log10(gdppcap)  1.13831    0.03288   34.62   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.2362 on 228 degrees of freedom
Multiple R-squared:  0.8402,    Adjusted R-squared:  0.8395 
F-statistic:  1199 on 1 and 228 DF,  p-value: < 2.2e-16

3.3 School Enrollment vs GDP Per Capita

  1. School enrollment, secondary (% gross): SE.SEC.ENRR

  2. GDP per capita, PPP (constant 2017 international $): NY.GDP.PCAP.PP.KD

  • School enrollment, secondary (% gross) Gross enrollment ratio is the ratio of total enrollment, regardless of age, to the population of the age group that officially corresponds to the level of education shown. Secondary education completes the provision of basic education that began at the primary level, and aims at laying the foundations for lifelong learning and human development, by offering more subject- or skill-oriented instruction using more specialized teachers. SE.SEC.ENRR

  • GDP per capita, PPP (constant 2017 international $) GDP per capita based on purchasing power parity (PPP). PPP GDP is gross domestic product converted to international dollars using purchasing power parity rates. An international dollar has the same purchasing power over GDP as the U.S. dollar has in the United States. GDP at purchaser’s prices is the sum of gross value added by all resident producers in the country plus any product taxes and minus any subsidies not included in the value of the products. It is calculated without making deductions for depreciation of fabricated assets or for depletion and degradation of natural resources. Data are in constant 2017 international dollars. ID: NY.GDP.PCAP.PP.KD

3.3.1 Importing Data

df_secgdp <- WDI(indicator = c(sec = "SE.SEC.ENRR", gdppcap = "NY.GDP.PCAP.PP.KD"), extra = TRUE)
write_csv(df_secgdp, "data/secgdp.csv")
df_secgdp <- read_csv("data/secgdp.csv")
Rows: 16758 Columns: 14── Column specification ─────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr  (7): country, iso2c, iso3c, region, capital, income, lending
dbl  (5): year, sec, gdppcap, longitude, latitude
lgl  (1): status
date (1): lastupdated
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.

3.3.2 Visualization by Line Graphs

COUNTRY <- "World"
df_secgdp |> filter(country == COUNTRY) |>
  ggplot(aes(year, sec)) + geom_line()

COUNTRIES <- c("Low income", "Low & middle income", "Lower middle income", "Middle income", "Upper middle income", "High income")
df_secgdp |> filter(country %in% COUNTRIES) |> drop_na(sec) |>
  ggplot(aes(year, sec, linetype = factor(country, levels = COUNTRIES))) + geom_line() +
  labs(linetype = "Income Levels")

3.3.2.1 Exercise.

Change COUNTRIES to ISO2C of countries you want to investigate. Use df_codes under Environment

df_secgdp |> filter(year == 2020) |> drop_na(sec) |>
  ggplot(aes(gdppcap, sec)) + geom_point()

df_secgdp |> filter(year == 2020) |> drop_na(gdppcap, sec) |>
  ggplot(aes(gdppcap, sec)) + geom_point() +
  scale_x_log10()

df_secgdp |> filter(year == 2020) |> drop_na(gdppcap, sec) |>
  ggplot(aes(gdppcap, sec)) + geom_point() +
  geom_smooth(method = "lm", formula = 'y~x', se = FALSE) +
  scale_x_log10()

df_secgdp |> filter(year == 2020) |> drop_na(gdppcap, sec) |>
  lm(sec~log10(gdppcap), data = _) |> summary()

Call:
lm(formula = sec ~ log10(gdppcap), data = drop_na(filter(df_secgdp, 
    year == 2020), gdppcap, sec))

Residuals:
    Min      1Q  Median      3Q     Max 
-53.777 -10.846  -1.173   9.006  66.996 

Coefficients:
               Estimate Std. Error t value Pr(>|t|)    
(Intercept)    -102.994     11.933  -8.631 6.38e-15 ***
log10(gdppcap)   46.088      2.841  16.222  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 15.64 on 157 degrees of freedom
Multiple R-squared:  0.6263,    Adjusted R-squared:  0.624 
F-statistic: 263.2 on 1 and 157 DF,  p-value: < 2.2e-16

4 Your Turn

4.1 Exercises.

Do a similar investigation by selecting WDI codes.

4.1.1 WDI Code

Choose at least two WDI codes with their names

  1. Name: Code:

  2. Name: Code:

4.1.2 Importing Data

Replace the following data_frame_name and shortname1, shortname2.

df_dataframe_name <- WDI(indicator = c(shortname1 = "", shortname2 = "", extra = TRUE))
write_csv(df_dataframe_name, "data/dataframe_name.csv")
df_dataframe_name <- read_csv("data/dataframe_name.csv")

4.1.3 Viewing Data

head(), str(), summary(), and try df_dataframe_name

4.1.4 Visualization

Try as many visualization as possible.

  • rank for each variable

  • line graph

  • scatterplot

  • scatterplot with a regression line

  • histogram

  • boxplot

4.2 References

  1. R for Data Science (2e): Link. The First Edition: Link.

  2. Posit Primers: Link.

  3. Cheat Sheet: Link.

LS0tCnRpdGxlOiAiSW50cm9kdWN0aW9uIHRvIFdESSwgUGFydCBJSSIKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIGRmX3ByaW50OiBwYWdlZAogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwogICAgcGFuZG9jX2FyZ3M6IC0tbnVtYmVyLW9mZnNldD0yCiAgd29yZF9kb2N1bWVudDoKICAgIHRvYzogeWVzCiAgICByZWZlcmVuY2VfZG9jeDogaW50cm8yd2RpX3RtcC5kb2N4CiAgcGRmX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKLS0tCgojIEV4cGxvcmF0b3J5IERhdGEgQW5hbHlzaXMgKEVEQSkKCiFbKlRoZSBpbWFnZSBhYm92ZSBpcyBmcm9tIFtSNERTKDJlKV0oaHR0cHM6Ly9yNGRzLmhhZGxleS5ueikgYnkgSGFkbGV5IFdpY2toYW0sIE1pbmUgw4dldGlua2F5YS1SdW5kZWwgYW5kIEdhcnJldHQgR3JvbGVtdW5kKl0oZGF0YS9kYXRhLXNjaWVuY2UucG5nKQoKKipFREEqKiBpcyBhbiBpdGVyYXRpdmUgY3ljbGUgdGhhdCBoZWxwcyB5b3UgdW5kZXJzdGFuZCB3aGF0IHlvdXIgZGF0YSBzYXlzLiBXaGVuIHlvdSBkbyBFREEsIHlvdToKCjEuICBHZW5lcmF0ZSBxdWVzdGlvbnMgYWJvdXQgeW91ciBkYXRhCgoyLiAgU2VhcmNoIGZvciBhbnN3ZXJzIGJ5IHZpc3VhbGlzaW5nLCB0cmFuc2Zvcm1pbmcsIGFuZC9vciBtb2RlbGluZyB5b3VyIGRhdGEKCjMuICBVc2Ugd2hhdCB5b3UgbGVhcm4gdG8gcmVmaW5lIHlvdXIgcXVlc3Rpb25zIGFuZC9vciBnZW5lcmF0ZSBuZXcgcXVlc3Rpb25zCgpFREEgaXMgYW4gaW1wb3J0YW50IHBhcnQgb2YgYW55IGRhdGEgYW5hbHlzaXMuIFlvdSBjYW4gdXNlIEVEQSB0byBtYWtlIGRpc2NvdmVyaWVzIGFib3V0IHRoZSB3b3JsZDsgb3IgeW91IGNhbiB1c2UgRURBIHRvIGVuc3VyZSB0aGUgcXVhbGl0eSBvZiB5b3VyIGRhdGEsIGFza2luZyBxdWVzdGlvbnMgYWJvdXQgd2hldGhlciB0aGUgZGF0YSBtZWV0cyB5b3VyIHN0YW5kYXJkcyBvciBub3QuIChQb3NpdCBQcmltZXJzOiBbRURBXShodHRwczovL3Bvc2l0LmNsb3VkL2xlYXJuL3ByaW1lcnMvMy4xKSkKCiMjIEdEUCBhbmQgR0RQIHBlciBDYXBpdGEKCjEuICBHRFAsIFBQUCAoY29uc3RhbnQgMjAxNyBpbnRlcm5hdGlvbmFsIFwkKTogTlkuR0RQLk1LVFAuUFAuS0QKCjIuICBQb3B1bGF0aW9uLCB0b3RhbDogU1AuUE9QLlRPVEwKCjMuICBDYWxjdWxhdGUgR0RQIHBlciBDYXBpdGEKCiAgICAtICAgR0RQIHBlciBjYXBpdGEsIFBQUCAoY29uc3RhbnQgMjAxNyBpbnRlcm5hdGlvbmFsIFwkKTogTlkuR0RQLlBDQVAuUFAuS0QKCi0gICBHRFAsIFBQUCAoY29uc3RhbnQgMjAxNyBpbnRlcm5hdGlvbmFsIFwkKSBQUFAgR0RQIGlzIGdyb3NzIGRvbWVzdGljIHByb2R1Y3QgY29udmVydGVkIHRvIGludGVybmF0aW9uYWwgZG9sbGFycyB1c2luZyBwdXJjaGFzaW5nIHBvd2VyIHBhcml0eSByYXRlcy4gQW4gaW50ZXJuYXRpb25hbCBkb2xsYXIgaGFzIHRoZSBzYW1lIHB1cmNoYXNpbmcgcG93ZXIgb3ZlciBHRFAgYXMgdGhlIFUuUy4gZG9sbGFyIGhhcyBpbiB0aGUgVW5pdGVkIFN0YXRlcy4gR0RQIGlzIHRoZSBzdW0gb2YgZ3Jvc3MgdmFsdWUgYWRkZWQgYnkgYWxsIHJlc2lkZW50IHByb2R1Y2VycyBpbiB0aGUgY291bnRyeSBwbHVzIGFueSBwcm9kdWN0IHRheGVzIGFuZCBtaW51cyBhbnkgc3Vic2lkaWVzIG5vdCBpbmNsdWRlZCBpbiB0aGUgdmFsdWUgb2YgdGhlIHByb2R1Y3RzLiBJdCBpcyBjYWxjdWxhdGVkIHdpdGhvdXQgbWFraW5nIGRlZHVjdGlvbnMgZm9yIGRlcHJlY2lhdGlvbiBvZiBmYWJyaWNhdGVkIGFzc2V0cyBvciBmb3IgZGVwbGV0aW9uIGFuZCBkZWdyYWRhdGlvbiBvZiBuYXR1cmFsIHJlc291cmNlcy4gRGF0YSBhcmUgaW4gY29uc3RhbnQgMjAxNyBpbnRlcm5hdGlvbmFsIGRvbGxhcnMuIElEOiBOWS5HRFAuTUtUUC5QUC5LRAoKLSAgIFBvcHVsYXRpb24sIHRvdGFsIFRvdGFsIHBvcHVsYXRpb24gaXMgYmFzZWQgb24gdGhlIGRlIGZhY3RvIGRlZmluaXRpb24gb2YgcG9wdWxhdGlvbiwgd2hpY2ggY291bnRzIGFsbCByZXNpZGVudHMgcmVnYXJkbGVzcyBvZiBsZWdhbCBzdGF0dXMgb3IgY2l0aXplbnNoaXAuIFRoZSB2YWx1ZXMgc2hvd24gYXJlIG1pZHllYXIgZXN0aW1hdGVzLiBJRDogU1AuUE9QLlRPVEwKCmBgYHtyIGNhY2hlID0gVFJVRSwgZXZhbCA9IEZBTFNFfQpkZl9nZHBwY2FwIDwtIFdESShpbmRpY2F0b3IgPSBjKGdkcCA9ICJOWS5HRFAuTUtUUC5QUC5LRCIsIHBvcCA9ICJTUC5QT1AuVE9UTCIsIGdkcHBjYXAgPSAiTlkuR0RQLlBDQVAuUFAuS0QiKSwgZXh0cmEgPSBUUlVFKQpgYGAKCmBgYHtyIGV2YWwgPSBGQUxTRX0Kd3JpdGVfY3N2KGRmX2dkcHBjYXAsICJkYXRhL2dkcHBjYXAuY3N2IikKYGBgCgpgYGB7cn0KZGZfZ2RwcGNhcCA8LSByZWFkX2NzdigiZGF0YS9nZHBwY2FwLmNzdiIpCmBgYAoKYGBge3J9CnN0cihkZl9nZHBwY2FwKQpgYGAKCmBgYHtyfQpkZl9nZHBwY2FwIHw+IHNlbGVjdChyZWdpb24sIGluY29tZSwgbGVuZGluZykgfD4gbGFwcGx5KHVuaXF1ZSkKYGBgCgpgYGB7cn0KQ09VTlRSWSA8LSAiV29ybGQiCmRmX2dkcHBjYXAgfD4gZmlsdGVyKGNvdW50cnkgPT0gQ09VTlRSWSkgfD4KICBnZ3Bsb3QoYWVzKHllYXIsIGdkcHBjYXApKSArIGdlb21fbGluZSgpCmBgYAoKYGBge3J9CkNPVU5UUlkgPC0gIldvcmxkIgpkZl9nZHBwY2FwIHw+IGZpbHRlcihjb3VudHJ5ID09IENPVU5UUlkpIHw+CiAgZ2dwbG90KGFlcyh5ZWFyLCBwb3ApKSArIGdlb21fbGluZSgpCmBgYAoKIyMjIyBFeGVyY2lzZS4KCldyaXRlIHlvdXIgb2JzZXJ2YXRpb25zIGFuZCBxdWVzdGlvbnMuCgojIyMgR0RQIFBlciBDYXBpdGEKCmBgYHtyfQpkZl9nZHBwY2FwMiA8LSBkZl9nZHBwY2FwIHw+IGRyb3BfbmEocG9wKSB8PiAKICBtdXRhdGUoUENBUCA9IGdkcC9wb3AsIC5hZnRlciA9IGdkcHBjYXApCmBgYAoKYGBge3J9CmRmX2dkcHBjYXAyCmBgYAoKIyMjIyBDaGVjayBhZ2FpbnN0IEdEUCBwZXIgY2FwaXRhLCBQUFAKCmBgYHtyIGV2YWwgPSBGQUxTRX0KZGZfZ2RwcGNhcDIgfD4gZHJvcF9uYShnZHBwY2FwLCBQQ0FQKSB8PiBtdXRhdGUobmVhciA9IG5lYXIoZ2RwcGNhcCwgUENBUCkpIHw+IAogIHN1bW1hcml6ZShudW1iZXJvZmRhdGEgPSBuKCksIHN1bShuZWFyKSkKYGBgCgpgYGB7cn0KZGZfZ2RwcGNhcDIgfD4gZmlsdGVyKCFuZWFyKGdkcHBjYXAsIFBDQVApKQpgYGAKCiMjIyMgRXhlcmNpc2UuCgpXcml0ZSB5b3VyIG9ic2VydmF0aW9ucyBhbmQgcXVlc3Rpb25zLgoKIyMjIFZpc3VhbGl6YXRpb24KClR3byB1c2VmdWwgcXVlc3Rpb25zLgoKMS4gIFdoYXQgdHlwZSBvZsKgKip2YXJpYXRpb24qKsKgb2NjdXJzwqAqKndpdGhpbioqwqBteSB2YXJpYWJsZXM/CgoyLiAgV2hhdCB0eXBlIG9mwqAqKmNvdmFyaWF0aW9uKirCoG9jY3Vyc8KgKipiZXR3ZWVuKirCoG15IHZhcmlhYmxlcz8KClNlZSBbTGlua10oaHR0cHM6Ly9wb3NpdC5jbG91ZC9sZWFybi9wcmltZXJzLzMuMSkuCgojIyMjIFJhbmtzLgoKYGFycmFuZ2UoZGVzYyhnZHApKWAgaXMgdG8gcmVvcmRlciBpbiBkZXNjZW5kaW5nIG9yZGVyIG9mIGBnZHAsYCBgYXJyYW5nZShnZHApYCBpbiBhc2NlbmRpbmcgb3JkZXIuCgpgYGB7cn0KZGZfZ2RwcGNhcCB8PiBmaWx0ZXIoeWVhciA9PSAyMDIyLCByZWdpb24gIT0gIkFnZ3JlZ2F0ZXMiKSB8PiAKICBkcm9wX25hKGdkcCkgfD4gYXJyYW5nZShkZXNjKGdkcCkpCmBgYAoKIyMjIyBFeGVyY2lzZXMuCgoxLiAgRmluZCB0aGUgdG9wIDEwIG9mIHRoZSBjb3VudHJpZXMgd2l0aCB0aGUgaGlnaGVzdCBHRFAgcGVyIGNhcGl0YS4KCjIuICBGaW5kIHRoZSB0b3AgMTAgb2YgdGhlIGNvdW50cmllcyB3aXRoIHRoZSBsb3dlc3QgR0RQIHBlciBjYXBpdGEuCgozLiAgRmluZCB0aGUgdG9wIDEwIG9mIHRoZSBjb3VudHJpZXMgd2l0aCB0aGUgbGFyZ2VzdCBwb3B1bGF0aW9uLgoKNC4gIEZpbmQgdGhlIHRvcCAxMCBvZiB0aGUgY291bnRyaWVzIHdpdGggdGhlIHNtYWxsZXN0IHBvcHVsYXRpb24uCgojIyMjIFNjYXR0ZXIgUGxvdAoKV2hhdCB0eXBlIG9mwqAqKmNvdmFyaWF0aW9uKirCoG9jY3Vyc8KgKipiZXR3ZWVuKirCoG15IHZhcmlhYmxlcz8KCmBgYHtyfQpkZl9nZHBwY2FwMiB8PiBmaWx0ZXIoeWVhciA9PSAyMDIyLCByZWdpb24gIT0iQWdncmVnYXRlcyIpIHw+IAogIGRyb3BfbmEoZ2RwLCBwb3ApIHw+IAogIGdncGxvdChhZXMocG9wLCBnZHApKSArIGdlb21fcG9pbnQoKQpgYGAKCmBgYHtyfQpkZl9nZHBwY2FwMiB8PiBmaWx0ZXIoeWVhciA9PSAyMDIyLCByZWdpb24gIT0iQWdncmVnYXRlcyIpIHw+IAogIGRyb3BfbmEoZ2RwLCBwb3ApIHw+IAogIGdncGxvdChhZXMocG9wLCBnZHApKSArIGdlb21fcG9pbnQoKSArIAogIHNjYWxlX3hfbG9nMTAoKSArIHNjYWxlX3lfbG9nMTAoKQpgYGAKCmBgYHtyfQpkZl9nZHBwY2FwMiB8PiBmaWx0ZXIoeWVhciA9PSAyMDIyLCByZWdpb24gIT0iQWdncmVnYXRlcyIpIHw+IAogIGRyb3BfbmEoZ2RwLCBwb3ApIHw+IAogIGdncGxvdChhZXMocG9wLCBnZHApKSArIGdlb21fcG9pbnQoKSArIAogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlID0gRkFMU0UpICsKICBzY2FsZV94X2xvZzEwKCkgKyBzY2FsZV95X2xvZzEwKCkKYGBgCgpgYGB7cn0KZGZfZ2RwcGNhcDIgfD4gZmlsdGVyKHllYXIgPT0gMjAyMCwgcmVnaW9uICE9IkFnZ3JlZ2F0ZXMiKSB8PiBkcm9wX25hKGdkcCwgcG9wKSB8PiAKICBnZ3Bsb3QoYWVzKHBvcCwgZ2RwLCBjb2xvciA9IHJlZ2lvbikpICsgZ2VvbV9wb2ludCgpICsgCiAgc2NhbGVfeF9sb2cxMCgpICsgc2NhbGVfeV9sb2cxMCgpCmBgYAoKYGBge3J9CmRmX2dkcHBjYXAyIHw+IGZpbHRlcih5ZWFyID09IDIwMjAsIHJlZ2lvbiAhPSJBZ2dyZWdhdGVzIikgfD4gCiAgZHJvcF9uYShnZHAsIHBvcCkgfD4gCiAgZ2dwbG90KGFlcyhwb3AsIGdkcCwgY29sb3IgPSByZWdpb24sIHNoYXBlID0gaW5jb21lKSkgKyBnZW9tX3BvaW50KCkgKyAKICBzY2FsZV94X2xvZzEwKCkgKyBzY2FsZV95X2xvZzEwKCkKYGBgCgpgYGB7cn0KZGZfZ2RwcGNhcDIgfD4gZmlsdGVyKHllYXIgPT0gMjAyMCwgcmVnaW9uICE9IkFnZ3JlZ2F0ZXMiKSB8PiAKICBkcm9wX25hKGdkcCwgZ2RwcGNhcCwgcG9wKSB8PiAKICBnZ3Bsb3QoYWVzKGdkcHBjYXAsIGdkcCwgY29sb3IgPSByZWdpb24sIHNpemUgPSBwb3ApKSArIGdlb21fcG9pbnQoKSArIAogIHNjYWxlX3hfbG9nMTAoKSArIHNjYWxlX3lfbG9nMTAoKQpgYGAKCmBgYHtyIGV2YWwgPSBGQUxTRX0KaW5zdGFsbC5wYWNrYWdlcygicGxvdGx5IikKYGBgCgpgYGB7cn0KbGlicmFyeShwbG90bHkpCnRlc3QgPC0gZGZfZ2RwcGNhcDIgfD4gZmlsdGVyKHllYXIgPT0gMjAyMCwgcmVnaW9uICE9IkFnZ3JlZ2F0ZXMiKSB8PiBkcm9wX25hKGdkcCwgcG9wKSB8PiAKICBnZ3Bsb3QoYWVzKGNvbG9yID0gY291bnRyeSwgc2hhcGUgPSByZWdpb24sIHBvcCwgZ2RwKSkgKyBnZW9tX3BvaW50KCkgKyAKICBzY2FsZV94X2xvZzEwKCkgKyBzY2FsZV95X2xvZzEwKCkgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCnRlc3QgfD4gZ2dwbG90bHkoKQpgYGAKCiMjIyMgVmFyaWF0aW9uIDEuIEhpc3RvZ3JhbQoKYGBge3J9CmRmX2dkcHBjYXAgfD4gZmlsdGVyKHllYXIgPT0gMjAyMiwgcmVnaW9uICE9ICJBZ2dyZWdhdGVzIikgfD4gZHJvcF9uYShnZHApIHw+IAogIGdncGxvdChhZXMoZ2RwKSkgKyBnZW9tX2hpc3RvZ3JhbSgpCmBgYAoKYGBge3J9CmRmX2dkcHBjYXAgfD4gZmlsdGVyKHllYXIgPT0gMjAyMiwgcmVnaW9uICE9ICJBZ2dyZWdhdGVzIikgfD4gZHJvcF9uYShnZHBwY2FwKSB8PiAKICBnZ3Bsb3QoYWVzKGdkcHBjYXApKSArIGdlb21faGlzdG9ncmFtKCkKYGBgCgpgYGB7cn0KZGZfZ2RwcGNhcCB8PiBmaWx0ZXIoeWVhciA9PSAyMDIyLCByZWdpb24gIT0gIkFnZ3JlZ2F0ZXMiKSB8PiBkcm9wX25hKGdkcCkgfD4gCiAgZ2dwbG90KGFlcyhnZHApKSArIGdlb21faGlzdG9ncmFtKCkgKyBzY2FsZV94X2xvZzEwKCkKYGBgCgojIyMjIEV4ZXJjaXNlcy4KCjEuICBDaGFuZ2UgYmlucywgaS5lLiwgYGdlb21faGlzdG9ncmFtKGJpbnMgPSAyMClgLCBldGMuCgpgYGB7ciBldmFsID0gRkFMU0V9CmRmX2dkcHBjYXAgfD4gZmlsdGVyKHllYXIgPT0gMjAyMiwgcmVnaW9uICE9ICJBZ2dyZWdhdGVzIikgfD4gZHJvcF9uYShnZHApIHw+IAogIGdncGxvdChhZXMoZ2RwKSkgKyBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMjApICsgc2NhbGVfeF9sb2cxMCgpCmBgYAoKMi4gIENyZWF0ZSBhIHNpbWlsYXIgaGlzdG9ncmFtIG9mIGdkcHBjYXAgYnkgdXNpbmcgYHNjYWxlX3hfbG9nMTAoKWAgYW5kIGFkanVzdCB0aGUgbnVtYmVyIG9mIGJpbnMuCgpgYGB7ciBldmFsID0gRkFMU0V9CmRmX2dkcHBjYXAgfD4gZmlsdGVyKHllYXIgPT0gMjAyMiwgcmVnaW9uICE9ICJBZ2dyZWdhdGVzIikgfD4gZHJvcF9uYShnZHBwY2FwKSB8PiAKICBnZ3Bsb3QoYWVzKGdkcHBjYXApKSArIGdlb21faGlzdG9ncmFtKCkgKyBzY2FsZV94X2xvZzEwKCkKYGBgCgozLiAgQ3JlYXRlIGEgc2ltaWxhciBoaXN0b2dyYW0gZm9yIHBvcHVsYXRpb24uCjQuICBXcml0ZSB5b3VyIG9ic2VydmF0aW9ucyBhbmQgY29tbWVudHMuCgojIyMjIEV4dHJhLgoKYGBge3J9CmRmX2dkcHBjYXAgfD4gZmlsdGVyKHllYXIgPT0gMjAyMixyZWdpb24gIT0gIkFnZ3JlZ2F0ZXMiKSB8PiBkcm9wX25hKHBvcCkgfD4gCiAgZ3JvdXBfYnkocmVnaW9uKSB8PiAKICBnZ3Bsb3QoYWVzKHBvcCwgZmlsbCA9IHJlZ2lvbikpICsgZ2VvbV9oaXN0b2dyYW0oY29sID0gImJsYWNrIiwgbGluZXdpZHRoID0gMC4yKSArIHNjYWxlX3hfbG9nMTAoKQpgYGAKCiMjIyMgVmFyaWF0aW9uIDIuIEJveHBsb3QKCmBgYHtyfQpkZl9nZHBwY2FwMiB8PiBmaWx0ZXIoeWVhciAlaW4lIGMoMTk5MCwyMDAwLCAyMDEwLCAyMDIwKSkgfD4gZHJvcF9uYShnZHBwY2FwKSB8PiAKICBnZ3Bsb3QoYWVzKGdkcHBjYXAsIGZhY3Rvcih5ZWFyKSkpICsgZ2VvbV9ib3hwbG90KCkgKyBzY2FsZV94X2xvZzEwKCkKYGBgCgpgYGB7cn0KZGZfZ2RwcGNhcDIgfD4gZmlsdGVyKHllYXIgJWluJSBjKDE5OTAsMjAwMCwgMjAxMCwgMjAyMCkpIHw+IGRyb3BfbmEoZ2RwcGNhcCkgfD4gCiAgZ2dwbG90KGFlcyhnZHBwY2FwLCBmYWN0b3IoeWVhcikpKSArIGdlb21fYm94cGxvdCgpICsgc2NhbGVfeF9sb2cxMCgpICsKICBsYWJzKHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiB0aGUgR0RQIHBlciBDYXBpdGEgb2YgQ291bnRyaWVzIiwgc3VidGl0bGUgPSAiWWVhciAxOTkwLCAyMDAwLCAyMDEwLCAyMDIwIiwgCiAgICAgICB5ID0gIlllYXIiLCB4ID0gIkdEUCBwZXIgY2FwaXRhIGluIGxvZzEwIHNjYWxlIikKYGBgCgpgYGB7cn0KZGZfZ2RwcGNhcDIgfD4gZmlsdGVyKHllYXIgPT0gMjAyMCkgfD4gZHJvcF9uYShnZHBwY2FwKSB8PiAKICBmaWx0ZXIoaW5jb21lICE9ICJBZ2dyZWdhdGVzIikgfD4gCiAgZ2dwbG90KGFlcyhnZHBwY2FwLCBpbmNvbWUsIGZpbGwgPSBpbmNvbWUpKSArIGdlb21fYm94cGxvdCgpICsgc2NhbGVfeF9sb2cxMCgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCmBgYAoKYGBge3J9CmRmX2dkcHBjYXAyIHw+IGZpbHRlcih5ZWFyID09IDIwMjApIHw+IGRyb3BfbmEoZ2RwcGNhcCkgfD4gCiAgZmlsdGVyKGluY29tZSAhPSAiQWdncmVnYXRlcyIpIHw+IAogIGdncGxvdChhZXMoZ2RwcGNhcCwgZmFjdG9yKGluY29tZSwgbGV2ZWxzID0gYygiSGlnaCBpbmNvbWUiLCAiVXBwZXIgbWlkZGxlIGluY29tZSIsICJMb3dlciBtaWRkbGUgaW5jb21lIiwgIkxvdyBpbmNvbWUiKSksIGZpbGwgPSBpbmNvbWUpKSArIGdlb21fYm94cGxvdCgpICsgc2NhbGVfeF9sb2cxMCgpICsKICBsYWJzKHkgPSAiIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKYGBgCgpgYGB7cn0KZGZfZ2RwcGNhcDIgfD4gZmlsdGVyKHllYXIgPT0gMjAyMCkgfD4gZHJvcF9uYShnZHApIHw+IAogIGZpbHRlcihpbmNvbWUgIT0gIkFnZ3JlZ2F0ZXMiKSB8PiAKICBnZ3Bsb3QoYWVzKGdkcCwgcmVnaW9uLCBmaWxsID0gcmVnaW9uKSkgKyBnZW9tX2JveHBsb3QoKSArIHNjYWxlX3hfbG9nMTAoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKCiMjIENPMiBFbWlzc2lvbnMgUGVyIENhcGl0YSB2cyBHRFAgUGVyIENhcGl0YQoKMS4gIENPMiBlbWlzc2lvbnMgKG1ldHJpYyB0b25zIHBlciBjYXBpdGEpOiBFTi5BVE0uQ08yRS5QQwoKMi4gIEdEUCBwZXIgY2FwaXRhLCBQUFAgKGNvbnN0YW50IDIwMTcgaW50ZXJuYXRpb25hbCBcJCk6IE5ZLkdEUC5QQ0FQLlBQLktECgotICAgQ08yIGVtaXNzaW9ucyAobWV0cmljIHRvbnMgcGVyIGNhcGl0YSkgQ2FyYm9uIGRpb3hpZGUgZW1pc3Npb25zIGFyZSB0aG9zZSBzdGVtbWluZyBmcm9tIHRoZSBidXJuaW5nIG9mIGZvc3NpbCBmdWVscyBhbmQgdGhlIG1hbnVmYWN0dXJlIG9mIGNlbWVudC4gVGhleSBpbmNsdWRlIGNhcmJvbiBkaW94aWRlIHByb2R1Y2VkIGR1cmluZyBjb25zdW1wdGlvbiBvZiBzb2xpZCwgbGlxdWlkLCBhbmQgZ2FzIGZ1ZWxzIGFuZCBnYXMgZmxhcmluZy4gRU4uQVRNLkNPMkUuUEMKCi0gICBHRFAgcGVyIGNhcGl0YSwgUFBQIChjb25zdGFudCAyMDE3IGludGVybmF0aW9uYWwgXCQpIEdEUCBwZXIgY2FwaXRhIGJhc2VkIG9uIHB1cmNoYXNpbmcgcG93ZXIgcGFyaXR5IChQUFApLiBQUFAgR0RQIGlzIGdyb3NzIGRvbWVzdGljIHByb2R1Y3QgY29udmVydGVkIHRvIGludGVybmF0aW9uYWwgZG9sbGFycyB1c2luZyBwdXJjaGFzaW5nIHBvd2VyIHBhcml0eSByYXRlcy4gQW4gaW50ZXJuYXRpb25hbCBkb2xsYXIgaGFzIHRoZSBzYW1lIHB1cmNoYXNpbmcgcG93ZXIgb3ZlciBHRFAgYXMgdGhlIFUuUy4gZG9sbGFyIGhhcyBpbiB0aGUgVW5pdGVkIFN0YXRlcy4gR0RQIGF0IHB1cmNoYXNlcidzIHByaWNlcyBpcyB0aGUgc3VtIG9mIGdyb3NzIHZhbHVlIGFkZGVkIGJ5IGFsbCByZXNpZGVudCBwcm9kdWNlcnMgaW4gdGhlIGNvdW50cnkgcGx1cyBhbnkgcHJvZHVjdCB0YXhlcyBhbmQgbWludXMgYW55IHN1YnNpZGllcyBub3QgaW5jbHVkZWQgaW4gdGhlIHZhbHVlIG9mIHRoZSBwcm9kdWN0cy4gSXQgaXMgY2FsY3VsYXRlZCB3aXRob3V0IG1ha2luZyBkZWR1Y3Rpb25zIGZvciBkZXByZWNpYXRpb24gb2YgZmFicmljYXRlZCBhc3NldHMgb3IgZm9yIGRlcGxldGlvbiBhbmQgZGVncmFkYXRpb24gb2YgbmF0dXJhbCByZXNvdXJjZXMuIERhdGEgYXJlIGluIGNvbnN0YW50IDIwMTcgaW50ZXJuYXRpb25hbCBkb2xsYXJzLiBJRDogTlkuR0RQLlBDQVAuUFAuS0QKCiMjIyBJbXBvcnRpbmcgRGF0YQoKYGBge3IgY2FjaGUgPSBUUlVFLCBldmFsID0gRkFMU0V9CmRmX2NvMmdkcCA8LSBXREkoaW5kaWNhdG9yID0gYyhjbzJwY2FwID0gIkVOLkFUTS5DTzJFLlBDIiwgZ2RwcGNhcCA9ICJOWS5HRFAuUENBUC5QUC5LRCIpLCBleHRyYSA9IFRSVUUpCmBgYAoKYGBge3IgZXZhbCA9IEZBTFNFfQp3cml0ZV9jc3YoZGZfY28yZ2RwLCAiZGF0YS9jbzJnZHAuY3N2IikKYGBgCgpgYGB7cn0KZGZfY28yZ2RwIDwtIHJlYWRfY3N2KCJkYXRhL2NvMmdkcC5jc3YiKQpgYGAKCiMjIyBWaXN1YWxpemF0aW9uIGJ5IExpbmUgR3JhcGhzCgpgYGB7cn0KQ09VTlRSWSA8LSAiV29ybGQiCmRmX2NvMmdkcCB8PiBmaWx0ZXIoY291bnRyeSA9PSBDT1VOVFJZKSB8PgogIGdncGxvdChhZXMoeWVhciwgY28ycGNhcCkpICsgZ2VvbV9saW5lKCkKYGBgCgpgYGB7cn0KSVNPMkMgPC0gYygiSlAiLCAiQ04iLCAiSUQiLCAiVUsiLCAiVVMiLCAiREUiLCAiRlIiKQpkZl9jbzJnZHAgfD4gZmlsdGVyKGlzbzJjICVpbiUgSVNPMkMpIHw+IGRyb3BfbmEoY28ycGNhcCkgfD4KICBnZ3Bsb3QoYWVzKHllYXIsIGNvMnBjYXAsIGxpbmV0eXBlID0gaXNvMmMpKSArIGdlb21fbGluZSgpCmBgYAoKIyMjIyBFeGVyY2lzZXMuCgoxLiAgQ2hhbmdlIGBpc28yY2AgY29kZXMgdG8gdGhvc2UgeW91IHdhbnQgdG8gaW52ZXN0aWdhdGUuIFVzZSBgZGZfY29kZXNgIHVuZGVyIEVudmlyb25tZW50CjIuICBDaGFuZ2UgYGxpbmV0eXBlYCB0byBjb2wuCgojIyMgU2NhdHRlcnBsb3QgZm9yIENvdmFyaWF0aW9uCgpgYGB7cn0KZGZfY28yZ2RwIHw+IGZpbHRlcih5ZWFyID09IDIwMjApIHw+IGRyb3BfbmEoY28ycGNhcCkgfD4KICBnZ3Bsb3QoYWVzKGdkcHBjYXAsIGNvMnBjYXApKSArIGdlb21fcG9pbnQoKQpgYGAKCmBgYHtyfQpkZl9jbzJnZHAgfD4gZmlsdGVyKHllYXIgPT0gMjAyMCkgfD4gCiAgZHJvcF9uYShnZHBwY2FwLCBjbzJwY2FwKSB8PgogIGdncGxvdChhZXMoZ2RwcGNhcCwgY28ycGNhcCkpICsgZ2VvbV9wb2ludCgpICsKICBzY2FsZV94X2xvZzEwKCkgKyBzY2FsZV95X2xvZzEwKCkKYGBgCgojIyMjIFNjYXR0ZXJwbG90IHdpdGggYSByZWdyZXNzaW9uIGxpbmUKCmBgYHtyfQpkZl9jbzJnZHAgfD4gZmlsdGVyKHllYXIgPT0gMjAyMCkgfD4gCiAgZHJvcF9uYShnZHBwY2FwLCBjbzJwY2FwKSB8PgogIGdncGxvdChhZXMoZ2RwcGNhcCwgY28ycGNhcCkpICsgZ2VvbV9wb2ludCgpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBmb3JtdWxhID0gJ3l+eCcsIHNlID0gRkFMU0UpICsKICBzY2FsZV94X2xvZzEwKCkgKyBzY2FsZV95X2xvZzEwKCkKYGBgCgojIyMjIFN1bW1hcnkgb2YgYSBsaW5lYXIgbW9kZWwKCmBgYHtyfQpkZl9jbzJnZHAgfD4gZmlsdGVyKHllYXIgPT0gMjAyMCkgfD4gZHJvcF9uYShnZHBwY2FwLCBjbzJwY2FwKSB8PgogIGxtKGxvZzEwKGNvMnBjYXApfmxvZzEwKGdkcHBjYXApLCBkYXRhID0gXykgfD4gc3VtbWFyeSgpCmBgYAoKIyMgU2Nob29sIEVucm9sbG1lbnQgdnMgR0RQIFBlciBDYXBpdGEKCjEuICBTY2hvb2wgZW5yb2xsbWVudCwgc2Vjb25kYXJ5ICglIGdyb3NzKTogU0UuU0VDLkVOUlIKCjIuICBHRFAgcGVyIGNhcGl0YSwgUFBQIChjb25zdGFudCAyMDE3IGludGVybmF0aW9uYWwgXCQpOiBOWS5HRFAuUENBUC5QUC5LRAoKLSAgIFNjaG9vbCBlbnJvbGxtZW50LCBzZWNvbmRhcnkgKCUgZ3Jvc3MpIEdyb3NzIGVucm9sbG1lbnQgcmF0aW8gaXMgdGhlIHJhdGlvIG9mIHRvdGFsIGVucm9sbG1lbnQsIHJlZ2FyZGxlc3Mgb2YgYWdlLCB0byB0aGUgcG9wdWxhdGlvbiBvZiB0aGUgYWdlIGdyb3VwIHRoYXQgb2ZmaWNpYWxseSBjb3JyZXNwb25kcyB0byB0aGUgbGV2ZWwgb2YgZWR1Y2F0aW9uIHNob3duLiBTZWNvbmRhcnkgZWR1Y2F0aW9uIGNvbXBsZXRlcyB0aGUgcHJvdmlzaW9uIG9mIGJhc2ljIGVkdWNhdGlvbiB0aGF0IGJlZ2FuIGF0IHRoZSBwcmltYXJ5IGxldmVsLCBhbmQgYWltcyBhdCBsYXlpbmcgdGhlIGZvdW5kYXRpb25zIGZvciBsaWZlbG9uZyBsZWFybmluZyBhbmQgaHVtYW4gZGV2ZWxvcG1lbnQsIGJ5IG9mZmVyaW5nIG1vcmUgc3ViamVjdC0gb3Igc2tpbGwtb3JpZW50ZWQgaW5zdHJ1Y3Rpb24gdXNpbmcgbW9yZSBzcGVjaWFsaXplZCB0ZWFjaGVycy4gU0UuU0VDLkVOUlIKCi0gICBHRFAgcGVyIGNhcGl0YSwgUFBQIChjb25zdGFudCAyMDE3IGludGVybmF0aW9uYWwgXCQpIEdEUCBwZXIgY2FwaXRhIGJhc2VkIG9uIHB1cmNoYXNpbmcgcG93ZXIgcGFyaXR5IChQUFApLiBQUFAgR0RQIGlzIGdyb3NzIGRvbWVzdGljIHByb2R1Y3QgY29udmVydGVkIHRvIGludGVybmF0aW9uYWwgZG9sbGFycyB1c2luZyBwdXJjaGFzaW5nIHBvd2VyIHBhcml0eSByYXRlcy4gQW4gaW50ZXJuYXRpb25hbCBkb2xsYXIgaGFzIHRoZSBzYW1lIHB1cmNoYXNpbmcgcG93ZXIgb3ZlciBHRFAgYXMgdGhlIFUuUy4gZG9sbGFyIGhhcyBpbiB0aGUgVW5pdGVkIFN0YXRlcy4gR0RQIGF0IHB1cmNoYXNlcidzIHByaWNlcyBpcyB0aGUgc3VtIG9mIGdyb3NzIHZhbHVlIGFkZGVkIGJ5IGFsbCByZXNpZGVudCBwcm9kdWNlcnMgaW4gdGhlIGNvdW50cnkgcGx1cyBhbnkgcHJvZHVjdCB0YXhlcyBhbmQgbWludXMgYW55IHN1YnNpZGllcyBub3QgaW5jbHVkZWQgaW4gdGhlIHZhbHVlIG9mIHRoZSBwcm9kdWN0cy4gSXQgaXMgY2FsY3VsYXRlZCB3aXRob3V0IG1ha2luZyBkZWR1Y3Rpb25zIGZvciBkZXByZWNpYXRpb24gb2YgZmFicmljYXRlZCBhc3NldHMgb3IgZm9yIGRlcGxldGlvbiBhbmQgZGVncmFkYXRpb24gb2YgbmF0dXJhbCByZXNvdXJjZXMuIERhdGEgYXJlIGluIGNvbnN0YW50IDIwMTcgaW50ZXJuYXRpb25hbCBkb2xsYXJzLiBJRDogTlkuR0RQLlBDQVAuUFAuS0QKCiMjIyBJbXBvcnRpbmcgRGF0YQoKYGBge3IgY2FjaGUgPSBUUlVFLCBldmFsID0gRkFMU0V9CmRmX3NlY2dkcCA8LSBXREkoaW5kaWNhdG9yID0gYyhzZWMgPSAiU0UuU0VDLkVOUlIiLCBnZHBwY2FwID0gIk5ZLkdEUC5QQ0FQLlBQLktEIiksIGV4dHJhID0gVFJVRSkKYGBgCgpgYGB7ciBldmFsID0gRkFMU0V9CndyaXRlX2NzdihkZl9zZWNnZHAsICJkYXRhL3NlY2dkcC5jc3YiKQpgYGAKCmBgYHtyfQpkZl9zZWNnZHAgPC0gcmVhZF9jc3YoImRhdGEvc2VjZ2RwLmNzdiIpCmBgYAoKIyMjIFZpc3VhbGl6YXRpb24gYnkgTGluZSBHcmFwaHMKCmBgYHtyfQpDT1VOVFJZIDwtICJXb3JsZCIKZGZfc2VjZ2RwIHw+IGZpbHRlcihjb3VudHJ5ID09IENPVU5UUlkpIHw+CiAgZ2dwbG90KGFlcyh5ZWFyLCBzZWMpKSArIGdlb21fbGluZSgpCmBgYAoKYGBge3J9CkNPVU5UUklFUyA8LSBjKCJMb3cgaW5jb21lIiwgIkxvdyAmIG1pZGRsZSBpbmNvbWUiLCAiTG93ZXIgbWlkZGxlIGluY29tZSIsICJNaWRkbGUgaW5jb21lIiwgIlVwcGVyIG1pZGRsZSBpbmNvbWUiLCAiSGlnaCBpbmNvbWUiKQpkZl9zZWNnZHAgfD4gZmlsdGVyKGNvdW50cnkgJWluJSBDT1VOVFJJRVMpIHw+IGRyb3BfbmEoc2VjKSB8PgogIGdncGxvdChhZXMoeWVhciwgc2VjLCBsaW5ldHlwZSA9IGZhY3Rvcihjb3VudHJ5LCBsZXZlbHMgPSBDT1VOVFJJRVMpKSkgKyBnZW9tX2xpbmUoKSArCiAgbGFicyhsaW5ldHlwZSA9ICJJbmNvbWUgTGV2ZWxzIikKYGBgCgojIyMjIEV4ZXJjaXNlLgoKQ2hhbmdlIGBDT1VOVFJJRVNgIHRvIGBJU08yQ2Agb2YgY291bnRyaWVzIHlvdSB3YW50IHRvIGludmVzdGlnYXRlLiBVc2UgYGRmX2NvZGVzYCB1bmRlciBFbnZpcm9ubWVudAoKYGBge3J9CmRmX3NlY2dkcCB8PiBmaWx0ZXIoeWVhciA9PSAyMDIwKSB8PiBkcm9wX25hKHNlYykgfD4KICBnZ3Bsb3QoYWVzKGdkcHBjYXAsIHNlYykpICsgZ2VvbV9wb2ludCgpCmBgYAoKYGBge3J9CmRmX3NlY2dkcCB8PiBmaWx0ZXIoeWVhciA9PSAyMDIwKSB8PiBkcm9wX25hKGdkcHBjYXAsIHNlYykgfD4KICBnZ3Bsb3QoYWVzKGdkcHBjYXAsIHNlYykpICsgZ2VvbV9wb2ludCgpICsKICBzY2FsZV94X2xvZzEwKCkKYGBgCgpgYGB7cn0KZGZfc2VjZ2RwIHw+IGZpbHRlcih5ZWFyID09IDIwMjApIHw+IGRyb3BfbmEoZ2RwcGNhcCwgc2VjKSB8PgogIGdncGxvdChhZXMoZ2RwcGNhcCwgc2VjKSkgKyBnZW9tX3BvaW50KCkgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIGZvcm11bGEgPSAneX54Jywgc2UgPSBGQUxTRSkgKwogIHNjYWxlX3hfbG9nMTAoKQpgYGAKCmBgYHtyfQpkZl9zZWNnZHAgfD4gZmlsdGVyKHllYXIgPT0gMjAyMCkgfD4gZHJvcF9uYShnZHBwY2FwLCBzZWMpIHw+CiAgbG0oc2VjfmxvZzEwKGdkcHBjYXApLCBkYXRhID0gXykgfD4gc3VtbWFyeSgpCmBgYAoKIyBZb3VyIFR1cm4KCiMjIEV4ZXJjaXNlcy4KCkRvIGEgc2ltaWxhciBpbnZlc3RpZ2F0aW9uIGJ5IHNlbGVjdGluZyBXREkgY29kZXMuCgojIyMgV0RJIENvZGUKCkNob29zZSBhdCBsZWFzdCB0d28gV0RJIGNvZGVzIHdpdGggdGhlaXIgbmFtZXMKCjEuICBOYW1lOiBDb2RlOgoKMi4gIE5hbWU6IENvZGU6CgojIyMgSW1wb3J0aW5nIERhdGEKClJlcGxhY2UgdGhlIGZvbGxvd2luZyBkYXRhX2ZyYW1lX25hbWUgYW5kIHNob3J0bmFtZTEsIHNob3J0bmFtZTIuCgpgYGB7ciBldmFsID0gRkFMU0V9CmRmX2RhdGFmcmFtZV9uYW1lIDwtIFdESShpbmRpY2F0b3IgPSBjKHNob3J0bmFtZTEgPSAiIiwgc2hvcnRuYW1lMiA9ICIiLCBleHRyYSA9IFRSVUUpKQpgYGAKCmBgYHtyIGV2YWwgPSBGQUxTRX0Kd3JpdGVfY3N2KGRmX2RhdGFmcmFtZV9uYW1lLCAiZGF0YS9kYXRhZnJhbWVfbmFtZS5jc3YiKQpgYGAKCmBgYHtyIGV2YWwgPSBGQUxTRX0KZGZfZGF0YWZyYW1lX25hbWUgPC0gcmVhZF9jc3YoImRhdGEvZGF0YWZyYW1lX25hbWUuY3N2IikKYGBgCgojIyMgVmlld2luZyBEYXRhCgpgaGVhZCgpYCwgYHN0cigpYCwgYHN1bW1hcnkoKWAsIGFuZCB0cnkgYGRmX2RhdGFmcmFtZV9uYW1lYAoKIyMjIFZpc3VhbGl6YXRpb24KClRyeSBhcyBtYW55IHZpc3VhbGl6YXRpb24gYXMgcG9zc2libGUuCgotICAgcmFuayBmb3IgZWFjaCB2YXJpYWJsZQoKLSAgIGxpbmUgZ3JhcGgKCi0gICBzY2F0dGVycGxvdAoKLSAgIHNjYXR0ZXJwbG90IHdpdGggYSByZWdyZXNzaW9uIGxpbmUKCi0gICBoaXN0b2dyYW0KCi0gICBib3hwbG90CgojIyBSZWZlcmVuY2VzCgoxLiAgUiBmb3IgRGF0YSBTY2llbmNlICgyZSk6IFtMaW5rXShodHRwczovL3I0ZHMuaGFkbGV5Lm56KS4gVGhlIEZpcnN0IEVkaXRpb246IFtMaW5rXShodHRwczovL3I0ZHMuaGFkLmNvLm56KS4KCjIuICBQb3NpdCBQcmltZXJzOiBbTGlua10oaHR0cHM6Ly9wb3NpdC5jbG91ZC9sZWFybi9wcmltZXJzKS4KCjMuICBDaGVhdCBTaGVldDogW0xpbmtdKGh0dHBzOi8vcG9zaXQuY2xvdWQvbGVhcm4vY2hlYXQtc2hlZXRzKS4K